#pragma once

#include <glibmm/ustring.h>
#include <glibmm/iochannel.h>
#include <stdio.h>

namespace BuildNG
{
  class OutStream
  {
  public:
    enum TextFormat
    {
      TextFormat_UTF8,
      TextFormat_UTF16,
      TextFormat_ASCII,
    };

    enum LineEndingFormat
    {
      /// The default, unix format, Works also with any decent Windows program, but does not with notepad.
      LineEndingFormat_Lf,
      /// Windows' default format.
      LineEndingFormat_CrLf,
      /// MacOS default format.
      LineEndingFormat_LfCr,
    };

    struct Settings
    {
      bool              insert_bom;
      TextFormat        text_format;
      LineEndingFormat  line_ending_format;

      Settings()
      {
        insert_bom = false;
        text_format = TextFormat_UTF8;
        line_ending_format = LineEndingFormat_Lf;
      }
    };

    static const int BUFFER_SIZE = 1024;

    OutStream(const Settings &settings = Settings());
    virtual ~OutStream();

    void write_uint32(int v);
    void write_uint32(guint32 v);
    void write_int32(int v);
    void write_uint16(int v);
    void write_int16(int v);
    void write_uint8(int v);
    void write_int8(int v);
    void write_bool(bool v);
    void write_line(const Glib::ustring &v);

    void write_buffer(const void *data,int buffer_size);

    void flush();

  protected:
    virtual void write_impl(const char *data,int buffer_size) = 0;

  private:
    char      buffer[BUFFER_SIZE];
    char*     write_ptr;

    Settings  settings;

    void      write_newline();
  };

  class FileOutStream : public OutStream
  {
  public:
    FileOutStream(const Glib::ustring &file_name);
    ~FileOutStream();

  protected:
    virtual void write_impl(const char *data,int buffer_size);

  private:
    FILE *file;
  };

  class MemoryOutStream : public OutStream
  {
  public:
    MemoryOutStream();
    ~MemoryOutStream();

    int           get_buffer_size() const;
    const void*   get_buffer() const;

  protected:
    virtual void  write_impl(const char *data,int buffer_size);

  private:
    size_t        size;
    size_t        reserved;
    void*         buffer;
  };

  class GIOChannelOutStream : public OutStream
  {
  public:
    GIOChannelOutStream(Glib::RefPtr<Glib::IOChannel> io_channel);
    ~GIOChannelOutStream();

  protected:
    virtual void write_impl(const char *data,int buffer_size);

  private:
    Glib::RefPtr<Glib::IOChannel> io_channel;
  };
  
    class StdOutStream : public OutStream
  {
  public:
    StdOutStream(std::ostream *stream,bool take_ownership);
    ~StdOutStream();

  protected:
    virtual void write_impl(const char *data,int buffer_size);

  private:
    std::ostream*   stream;
    bool            owned;
  };
}

